//
//  LBCombineTextureFilter.mm
//  OpenGLGame
//
//  Created by Nguyen Cong Huy on 2/27/13.
//  Copyright (c) 2013 UIT. All rights reserved.
//

#include "LBCombineTextureFilter.h"
#include "LBProgram.h"
#include "LBGraphics.h"
#include "LBTexture.h"

char combineFragShader[]=""

"precision mediump float;  "

"varying highp vec2 vtexCoor;"
"varying highp vec2 vtexCoor2;"
"varying highp vec2 vtexCoor3;"

"uniform sampler2D texture;"
"uniform sampler2D texture2; "
"uniform sampler2D texture3; "
"varying vec4 vposition;"

"uniform vec4 averageSkinColor;"
"uniform int numFace;"
"uniform float standardBrightness;"

"uniform float imageWidthFactor;"
"uniform float imageHeightFactor;"
"uniform float sharpness;"

//contrast function (default 1.0)
"vec4 contrastColor(vec4 color, float contrast){"
"color.r = (color.r - 0.5)*contrast + 0.5;"
"color.g = (color.g - 0.5)*contrast + 0.5;"
"color.b = (color.b - 0.5)*contrast + 0.5;"
"color = clamp(color, 0.0, 1.0);"
"return color;"
"}"
//END contrast function

//brightness function (default 0.0)
"vec4 brightnessColor(vec4 color, float brightness){"
"color.r = color.r + brightness;"
"color.g = color.g + brightness;"
"color.b = color.b + brightness;"
"color = clamp(color, 0.0, 1.0);"
"return color;"
"}"
//END brightness function

"float calculateBrightness(vec4 color, float standardBrightness){"
"   float averageSumRGB = averageSkinColor.r + averageSkinColor.g + averageSkinColor.b;"
"   float sumRGB = color.r + color.g + color.b;"
"   float amplititude = 2.0;"
"   float brightness = standardBrightness*(1.0 - abs(sumRGB - averageSumRGB)/amplititude);"
"   if(brightness < 0.0) brightness = 0.0;"
"   return brightness;"
"}"

"float calculateBrightnessDefault(vec4 color, float standardBrightness){"
"   vec4 averageSkinColor = vec4(0.66, 0.47, 0.42, 1.0);"
"   float averageSumRGB = averageSkinColor.r + averageSkinColor.g + averageSkinColor.b;"
"   float sumRGB = color.r + color.g + color.b;"
"   float amplititude = 2.0;"
"   float brightness = standardBrightness*(1.0 - abs(sumRGB - averageSumRGB)/amplititude);"
"   if(brightness < 0.0) brightness = 0.0;"
"   return brightness;"
"}"


"vec4 normalColor(vec4 base, vec4 blend, float opacity){"
"   vec4 result = mix(base, blend, opacity);"
"   return result;"
"}"

"vec4 sharpen(vec4 color, float sharpness, sampler2D texture, vec2 texCoor, float texelWidth, float texelHeight){"
"   mediump vec2 widthStep = vec2(imageWidthFactor, 0.0);"
"   mediump vec2 heightStep = vec2(0.0, imageHeightFactor);"

"   mediump vec2 leftTextureCoordinate = texCoor.xy - widthStep;"
"   mediump vec2 rightTextureCoordinate = texCoor.xy + widthStep;"
"   mediump vec2 topTextureCoordinate = texCoor.xy + heightStep;"
"   mediump vec2 bottomTextureCoordinate = texCoor.xy - heightStep;"

"   float centerMultiplier = 1.0 + 4.0 * sharpness;"
"   float edgeMultiplier = sharpness;"

"   mediump vec3 leftTextureColor = texture2D(texture, leftTextureCoordinate).rgb;"
"   mediump vec3 rightTextureColor = texture2D(texture, rightTextureCoordinate).rgb;"
"   mediump vec3 topTextureColor = texture2D(texture, topTextureCoordinate).rgb;"
"   mediump vec3 bottomTextureColor = texture2D(texture, bottomTextureCoordinate).rgb;"

"   vec4 result = vec4((color.rgb * centerMultiplier - (leftTextureColor * edgeMultiplier + rightTextureColor * edgeMultiplier + topTextureColor * edgeMultiplier + bottomTextureColor * edgeMultiplier)), texture2D(texture, bottomTextureCoordinate).w);"
"   return result;"

"}"

"void main()"
"{"

"    vec4 textureColor = texture2D(texture, vtexCoor);"                 //  origin
"    lowp vec4 texture2Color = texture2D(texture2, vtexCoor);"          //  blur
"    lowp vec4 texture3Color = texture2D(texture3, vtexCoor);"          //  mask

//"   textureColor = sharpen(textureColor, sharpness*0.5, texture, vtexCoor, imageWidthFactor, imageHeightFactor);"
"   texture2Color = sharpen(texture2Color, sharpness, texture2, vtexCoor, imageWidthFactor, imageHeightFactor);"


"   if(numFace > 0){"
"       textureColor = brightnessColor(textureColor, calculateBrightness(textureColor, standardBrightness));"
"       texture2Color = brightnessColor(texture2Color, calculateBrightness(texture2Color, standardBrightness));"
"   }"
"   else{"
"       textureColor = brightnessColor(textureColor, calculateBrightnessDefault(textureColor, standardBrightness));"
"       texture2Color = brightnessColor(texture2Color, calculateBrightnessDefault(texture2Color, standardBrightness));"
"   }"


"    vec4 result = textureColor;"
"    if(texture3Color.r > 0.9) result = normalColor(textureColor, texture2Color, 0.7);"
"    gl_FragColor = result;"

//"    gl_FragColor = texture3Color;"           // test skin detect
//"   gl_FragColor = sharpen(textureColor, 4.0, texture, vtexCoor, imageWidthFactor, imageHeightFactor);"

"}";


LBCombineTextureFilter::LBCombineTextureFilter(){
    init(0, combineFragShader);
    LBGraphics::getInstance()->useProgram(program);
    averageSkinColorUniform = program->uniformIndex("averageSkinColor");
    numFaceUniform = program->uniformIndex("numFace");
    standardBrightnessUniform = program->uniformIndex("standardBrightness");
    imageWidthFactorUniform = program->uniformIndex("imageWidthFactor");
    imageHeightFactorUniform = program->uniformIndex("imageHeightFactor");
    sharpnessUniform = program->uniformIndex("sharpness");
    setStandardBrightness(0.1);
    setSharpness(1.0);
}

void LBCombineTextureFilter::setBackgroundTexture(LBTexture* texture){
    setTexture(texture);
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(imageWidthFactorUniform, 1.0/texture->getWidth());
    glUniform1f(imageHeightFactorUniform, 1.0/texture->getHeight());
}

void LBCombineTextureFilter::setTopTexture(LBTexture* texture){
    setTexture2(texture);
}

void LBCombineTextureFilter::setMaskTexture(LBTexture* texture){
    setTexture3(texture);
}

void LBCombineTextureFilter::setAverageSkinColor(float red, float green, float blue){
    LBGraphics::getInstance()->useProgram(program);
    averageSkinColor[0] = red;
    averageSkinColor[1] = green;
    averageSkinColor[2] = blue;
    averageSkinColor[3] = 1.0;
    LBGraphics::getInstance()->useProgram(program);
    glUniform4f(averageSkinColorUniform, red, green, blue, 1.0);
}

void LBCombineTextureFilter::setNumFace(int numFace){
    this->numFace = numFace;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1i(numFaceUniform, numFace);
}

void LBCombineTextureFilter::setStandardBrightness(float standardBrightness){
    this->standardBrightness = standardBrightness;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(standardBrightnessUniform, standardBrightness);
}

void LBCombineTextureFilter::setSharpness(float sharpeness){
    this->sharpness = sharpeness;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(sharpnessUniform, sharpeness);
}